package com.graduation.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.graduation.common.autoconfigure.auth.JwtProperties;
import com.graduation.common.constans.BizConstants;
import com.graduation.common.constans.RedisConstants;
import com.graduation.common.domain.dto.PageDTO;
import com.graduation.common.enums.StatusEnum;
import com.graduation.common.exceptions.BizException;
import com.graduation.common.utils.JwtUtil;
import com.graduation.common.utils.MD5Util;
import com.graduation.mapper.UserMapper;
import com.graduation.pojo.dto.ChangePasswordDTO;
import com.graduation.pojo.dto.LoginUserDTO;
import com.graduation.pojo.po.User;
import com.graduation.pojo.query.UserQuery;
import com.graduation.pojo.vo.UserVO;
import com.graduation.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final StringRedisTemplate stringRedisTemplate;

    private final JwtProperties jwtProperties;

    @Override
    public String login(LoginUserDTO body) {
        String username = body.getUsername();
        String password = body.getPassword();
        Integer role = body.getRole();
        // 信息校验
        if (StrUtil.hasBlank(username, password)) {
            throw new BizException("用户名和密码都不能为空");
        }
        if (role == null) {
            throw new BizException("登录角色未携带");
        }

        // 获取登录用户
        User user = lambdaQuery()
                .eq(User::getUsername, username)
                .eq(User::getRole, role)
                .one();
        if (user == null) {
            throw new BizException("登录用户为空");
        }
        if (user.getStatus() == StatusEnum.OFF) {
            throw new BizException("用户已停用");
        }

        // 校验密码
        String saltPassword = MD5Util.encodeWithSalt(password, user.getSalt());
        if (!saltPassword.equals(user.getPassword())) {
            throw new BizException("密码错误");
        }

        // 保存登录信息
        Long userId = user.getId();
        String uuid = UUID.fastUUID().toString(true);
        String key = RedisConstants.LOGIN_CACHE_KEY_PREFIX + uuid;
        stringRedisTemplate.opsForValue().set(key, userId.toString(), jwtProperties.getTokenTtl(), TimeUnit.MINUTES);

        // 创建token返回
        Map<String, Object> map = new HashMap<>();
        map.put(BizConstants.TOKEN_LOGIN_INFO, uuid);
        return JwtUtil.createToken(map, jwtProperties.getPrivateKey(), jwtProperties.getTokenTtl());
    }

    @Override
    public PageDTO<UserVO> pageUsers(UserQuery query) {
        // 构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StrUtil.isNotBlank(query.getUsername()), User::getUsername, query.getUsername())
                .eq(query.getRole() != null, User::getRole, query.getRole())
                .eq(query.getStatus() != null, User::getStatus, query.getStatus())
                .orderByDesc(User::getCreateTime);

        // 分页查询
        Page<User> page = page(query.toMpPageByCreateTime(), wrapper);
        
        // 转换为VO并返回PageDTO
        List<UserVO> voList = page.getRecords().stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
        
        return PageDTO.of(page.getPages(), page.getTotal(), voList);
    }

    @Override
    public UserVO getUserById(Long id) {
        User user = getById(id);
        if (user == null) {
            throw new BizException("用户不存在");
        }
        return convertToVO(user);
    }

    @Override
    public void saveUser(User user) {
        // 检查用户名是否已存在
        boolean exists = lambdaQuery()
                .eq(User::getUsername, user.getUsername())
                .exists();
        if (exists) {
            throw new BizException("用户名已存在");
        }

        // 生成盐值和加密密码
        String salt = UUID.fastUUID().toString(true);
        String encodedPassword = MD5Util.encodeWithSalt(user.getPassword(), salt);
        
        user.setPassword(encodedPassword);
        user.setSalt(salt);
        
        save(user);
    }

    @Override
    public void updateUser(User user) {
        User existUser = getById(user.getId());
        if (existUser == null) {
            throw new BizException("用户不存在");
        }

        // 检查用户名是否被其他用户使用
        if (StrUtil.isNotBlank(user.getUsername()) && 
            !user.getUsername().equals(existUser.getUsername())) {
            boolean exists = lambdaQuery()
                    .eq(User::getUsername, user.getUsername())
                    .ne(User::getId, user.getId())
                    .exists();
            if (exists) {
                throw new BizException("用户名已存在");
            }
        }

        // 如果修改了密码，需要重新加密
        if (StrUtil.isNotBlank(user.getPassword()) && 
            !user.getPassword().equals(existUser.getPassword())) {
            String salt = UUID.fastUUID().toString(true);
            String encodedPassword = MD5Util.encodeWithSalt(user.getPassword(), salt);
            user.setPassword(encodedPassword);
            user.setSalt(salt);
        }
        
        updateById(user);
    }

    @Override
    public void deleteUser(Long id) {
        User user = getById(id);
        if (user == null) {
            throw new BizException("用户不存在");
        }
        removeById(id);
    }

    @Override
    public void changePassword(Long userId, ChangePasswordDTO changePasswordDTO) {
        // 参数校验
        if (StrUtil.hasBlank(changePasswordDTO.getOldPassword(), changePasswordDTO.getNewPassword(), changePasswordDTO.getConfirmPassword())) {
            throw new BizException("旧密码、新密码和确认密码都不能为空");
        }
        
        // 验证新密码和确认密码是否一致
        if (!changePasswordDTO.getNewPassword().equals(changePasswordDTO.getConfirmPassword())) {
            throw new BizException("新密码和确认密码不一致");
        }
        
        // 获取用户信息
        User user = getById(userId);
        if (user == null) {
            throw new BizException("用户不存在");
        }
        
        // 验证旧密码是否正确
        String oldPasswordEncoded = MD5Util.encodeWithSalt(changePasswordDTO.getOldPassword(), user.getSalt());
        if (!oldPasswordEncoded.equals(user.getPassword())) {
            throw new BizException("旧密码不正确");
        }
        
        // 生成新的盐值和加密新密码
        String newSalt = UUID.fastUUID().toString(true);
        String newPasswordEncoded = MD5Util.encodeWithSalt(changePasswordDTO.getNewPassword(), newSalt);
        
        // 更新用户密码和盐值
        user.setPassword(newPasswordEncoded);
        user.setSalt(newSalt);
        updateById(user);
    }

    /**
     * 转换为VO对象
     */
    private UserVO convertToVO(User user) {
        UserVO vo = new UserVO();
        BeanUtil.copyProperties(user, vo);
        return vo;
    }
}